<?php
namespace app\common\redis;

use app\common\library\CommonFun;
use app\common\model\article\PublishArticleCoinModel;
use app\common\model\article\PublishArticleEssenceModel;
use app\common\model\article\PublishArticleModel;
use app\common\model\article\PublishWeiboModel;
use app\common\model\pair\PairRuntimeModel;
use app\common\model\recommend\RecommendArticleEssenceModel;
use app\common\model\recommend\RecommendArticleTopModel;
use app\common\model\user\UserCoinModel;
use app\common\model\user\UserPairModel;
use app\common\service\PythonApiService;

class UserReadRedis extends RedisModel {
	/**
	 * Redis配置名称
	 * @var string
	 */
	public $redis = 'default';

	/**
	 * Redis key
	 * @var string
	 */
	public $key = '';

	/**
	 * Redis key 前缀
	 */
	public $key_prefix = 'user_read';
	
	/**
	 * 用户已读文章
	 * @var array
	 */
	var $userReadArticleList = [];

	/**
	 * 获取用户读取的文章列表
	 * @param int $user_id 用户编号
	 */
	public function GetUserReadArtIDList($user_id) {
		$this->SetRedisKey($user_id);
		$res = $this->SetGetAll();
		return $res;
	}

	/**
	 * 设置用户已阅读文章编号
	 * @param int $user_id 用户编号
	 * @param int|array $art_id 文章编号
	 * @return
	 */
	public function SetUserReadArtID($user_id, $art_id) {
		$this->SetRedisKey($user_id);
		if ( is_array($art_id) ) {
			$this->PipeStart();
			foreach ( $art_id as $item ) {
				$this->SetSadd($item);
			}
			$res = $this->PipeExec();
		}else {
			$res = $this->SetSadd($art_id);
		}
		return $res;
	}

	/**
	 * 删除用户已阅读数据
	 * @param int $user_id 用户编号
	 */
	public function DeleteUserRead($user_id) {
		$this->SetRedisKey($user_id);
		if ( $this->ExistsKey() ) {
			$this->Delete();
		}
	}

	/**
	 * 创建新用户文章列表
	 * @param int $user_id 用户编号
	 * @param string $client_sn 用户客户端唯一编号
	 */
	public function CreateNewUserArticleList( $user_id, $client_sn ) {
		// 用户币种模型
		$modelUserCoin = UserCoinModel::getInstance();
		$modelUserPair  = UserPairModel::getInstance();
		// Python接口模型
		$servicePython = PythonApiService::getInstance();
		// 用户币id列表
		$user_coin_id_list = array();
		// 用户持仓币列表
		$user_asset_coin_id_list = $servicePython->getUserAssetCoinID($user_id);
		Log::write("用户持仓币列表:".var_export($user_asset_coin_id_list, true));
		// 用户关注币列表
		$user_pair_coin_id_list = array();
		$user_pair_id_list = $modelUserPair->get_user_pair_id_list($user_id);
		if ( $user_pair_id_list ) {
			$modePair = PairRuntimeModel::getInstance();
			$pair_data = $modePair->muti_get_data_by_id_list($user_pair_id_list);
			$user_pair_coin_id_list = array_column($pair_data, "base");
			Log::write("用户关注币列表:".var_export($user_pair_coin_id_list, true));
		}
		// 用户所有币种编号列表
		$user_coin_id_list = array_unique(array_merge($user_asset_coin_id_list, $user_pair_coin_id_list));
		// 新用户关注币种执行创建用户文章的列表
		if ( $user_coin_id_list ) {
			return $this->CreateUserArticleList($user_id, $client_sn, $user_coin_id_list);
		}

		// 用户已读文章数据
		$user_mark = $user_id.":".$client_sn;
		$userReadArticleList = $this->getUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);

		// 获取置顶文章
		$modelTop = RecommendArticleTopModel::getInstance();
		$topData = $modelTop->getAllTopDataByTime();
		$top_aw_id_list = [];
		if ( $topData ) {
			foreach ( $topData as $v ) {
				// 文章类型 并且 不在已读文章
				if ( $v['type'] == RecommendArticleTopModel::TypeArticle &&  !in_array($v['article_id'], $userReadArticleList) ) {
					$top_aw_id_list[] = RecommendArticleTopModel::TypeArticle."_".$v['article_id'];
				}
			}
		}

		// 置顶文章
		if ( $top_aw_id_list ) {
			// 文章列表Label 配置
			$label_cfg = PublishArticleModel::ArticleLabelConfig['top'];
			foreach ( $top_aw_id_list as $aw_id ) {
				if ( isset($top_aw_list[$aw_id]) ) {
					continue;
				}
				$unique_key = CommonFun::create_unique_id();
				list( $type, $id ) = explode("_", $aw_id);
				$top_aw_list[$aw_id] = array(
						"unique_key" => $unique_key,
						"type" => $type,
						"id" => $id,
						"coin_id" => 0,
						"label_name" => $label_cfg['label_name'],
						"label_icon" => $label_cfg['label_icon'],
						"user_coin_id_list" => [],
				);
			}
		}

		// 获取48小时内的所有文章
		$now = time(); // 当前时间
		$endDate = date( "Y-m-d H:i:s", $now ); // 结束日期
		$startDate = date( "Y-m-d H:i:s", $now-(3600*48) ); // 开始时间
		// 文章模型
		$modelArticle = PublishArticleModel::getInstance();
		$article_map = array(
				'is_published' => 1,
				'status' => 1,
				'deleted' => -1,
				'is_feed' => 1,
				'create_time' => array('between', array( $startDate, $endDate )),
				'is_tmp' => -1,
		);
		if ( $userReadArticleList ) {
			$article_map['id'] = array( 'not in', $userReadArticleList );
		}
		// 文章编号列表
		$article_id_list = $modelArticle->search_id_list_no_page( $article_map, 'id' , false, 'create_time' );
		$article_aw_id_list = [];
		if ( $article_id_list ) {
			// 获取文章点击量
			$MongoArticleStatisticsClick = new ArticleStatisticsClickMongo();
			$article_count = $MongoArticleStatisticsClick->getArticleClickNumByList($article_id_list);
			$article_id_list = array_keys($article_count);
			foreach ( $article_id_list as $haid ) {
				$aw = RecommendArticleTopModel::TypeArticle."_".$haid;
				if ( !in_array($aw, $top_aw_id_list) ) {
					$article_aw_id_list[] = $aw;
				}
			}
		}

		// 48小时文章处理
		$article_aw_list = [];
		if ( $article_aw_id_list ) {
			// 文章列表Label 配置
			foreach ( $article_aw_id_list as $aw_id ) {
				if ( isset($article_aw_list[$aw_id]) ) {
					continue;
				}
				$unique_key = CommonFun::create_unique_id();
				list( $type, $id ) = explode("_", $aw_id);
				$article_aw_list[$aw_id] = array(
						"unique_key" => $unique_key,
						"type" => $type,
						"id" => $id,
						"coin_id" => 0,
						"label_name" => '',
						"label_icon" => '',
						"user_coin_id_list" => [],
				);
			}
		}

		$hash_list = [];
		// 取数置顶数据
		if ( $top_aw_list ) {
			foreach ( $top_aw_list as $k=>$v ) {
				$hash_list[$k] = CommonFun::json_encode2($v);
			}
		}

		// 48小时文章
		if ( $article_aw_list ) {
			foreach ( $article_aw_list as $k=>$v ) {
				if ( isset( $hash_list[$k] ) ) {
					continue;
				}
				$hash_list[$k] = CommonFun::json_encode2($v);
			}
		}

		// 文章Hash数据
		$key = "UserArticleHashTable:{$user_id}:{$client_sn}";
		$this->SetRedisKey($key);
		if ( $this->ExistsKey() ) {
			$this->Delete();
		}
		if ($hash_list) {
			$this->ListMutiRpush($hash_list);
			return true;
		}else {
			// 没有文章Hash数据重建数据
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeWeiBo);
			return false;
		}
	}

	/**
	 * 创建用户文章的列表
	 * @param int $user_id 用户编号
	 * @param string $client_sn 用户客户端唯一编号
	 * @param array $special_user_coin_id_list 指定用户持币列表 (主要用于非登录持币推荐)
	 */
	public function CreateUserArticleList( $user_id, $client_sn, array $special_user_coin_id_list=array() ) {
		// 用户币种模型
		$modelUserCoin = UserCoinModel::getInstance();
		$modelUserPair  = UserPairModel::getInstance();
		// 文章模型
		$modelArticle = PublishArticleModel::getInstance();
		$modelWeibo = PublishWeiboModel::getInstance();
		$modelTop = RecommendArticleTopModel::getInstance();
		$modelEssence = RecommendArticleEssenceModel::getInstance();
		// Python接口模型
		$servicePython = PythonApiService::getInstance();
		// 用户已读文章数据
		$user_mark = $user_id.":".$client_sn;
		$userReadArticleList = $this->getUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);
		$userReadWeiboList = $this->getUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeWeiBo);
		// 获取置顶文章
		$topData = $modelTop->getAllTopDataByTime();
		$top_aw_id_list = [];
		if ( $topData ) {
			foreach ( $topData as $v ) {
				if ( $v['type'] == RecommendArticleTopModel::TypeArticle ) {
					if ( !in_array($v['article_id'], $userReadArticleList) ) {
						$top_aw_id_list[] = RecommendArticleTopModel::TypeArticle."_".$v['article_id'];
					}
				}elseif ( $v['type'] == RecommendArticleTopModel::TypeWeiBo ) {
					if ( !in_array($v['article_id'], $userReadWeiboList) ) {
						$top_aw_id_list[] = RecommendArticleTopModel::TypeWeiBo."_".$v['article_id'];
					}
				}
			}
		}
		// 获取加精文章
		$essenceData = $modelEssence->getAllEssenceData();
		$essence_aw_id_list = [];
		if ( $essenceData ) {
			foreach ( $essenceData as $v ) {
				if ( $v['type'] == RecommendArticleEssenceModel::TypeArticle ) {
					$aw = RecommendArticleEssenceModel::TypeArticle."_".$v['article_id'];
					if ( !in_array($v['article_id'], $userReadArticleList) && !in_array($aw, $top_aw_id_list) ) {
						$essence_aw_id_list[] = $aw;
					}
				}elseif ( $v['type'] == RecommendArticleEssenceModel::TypeWeiBo ) {
					$aw = RecommendArticleEssenceModel::TypeWeiBo."_".$v['article_id'];
					if ( !in_array($v['article_id'], $userReadWeiboList) && !in_array($aw, $top_aw_id_list) ) {
						$essence_aw_id_list[] = $aw;
					}
				}
			}
		}
		// 用户持仓币列表
		$user_asset_coin_id_list = [];
		// 用户币id列表
		$user_coin_id_list = array();
		if ($special_user_coin_id_list) {
			$user_coin_id_list = $special_user_coin_id_list;
		}else {
			if ( $user_id ) {
				// 用户持仓币列表
				$user_asset_coin_id_list = $servicePython->getUserAssetCoinID($user_id);
				Log::write("用户持仓币列表:".var_export($user_asset_coin_id_list, true));
				// 用户关注币列表
				$user_pair_coin_id_list = array();
				$user_pair_id_list = $modelUserPair->get_user_pair_id_list($user_id);
				if ( $user_pair_id_list ) {
					$modePair = PairRuntimeModel::getInstance();
					$pair_data = $modePair->muti_get_data_by_id_list($user_pair_id_list);
					$user_pair_coin_id_list = array_column($pair_data, "base");
				}

				// 用户所有币种编号列表
				$user_coin_id_list = array_unique(array_merge($user_asset_coin_id_list, $user_pair_coin_id_list));
			}
		}
		// 未关注币种的用户，将BTC和ETH视为默认关注
		if ( !$user_coin_id_list ) {
			$user_coin_id_list = array(301, 617);
		}

		// 取出24小时的文章
		$todayStartEndTime = CommonFun::getTodayStartEndTime();
		$end = $todayStartEndTime['end'];
		$endDate = date( "Y-m-d H:i:s", $end );
		$start =  $todayStartEndTime['start']-(86400*3);
		$startDate =  date( "Y-m-d H:i:s", $start );
		$noCoinStartDate = date( "Y-m-d H:i:s", $todayStartEndTime['start']-(86400*3) );

		// 文章编号总列表
		$all_article_id_list = [];
		// 微博编号总列表
		$all_weibo_id_list = [];
		// 关注币种加精文章列表
		$asset_aw_list = [];
		// 关注币种非加精文章列表
		$asset_aw_unessence_list = [];
		// 置顶文章列表
		$top_aw_list = [];
		// 热门文章列表(非加精)
		$hot_aw_list = [];
		// 热门加精文章
		$hot_essence_aw_list = [];

		// 用户币种aw列表
		$user_coin_aw_list = [];
		// 存在用户币种, 获取用户币相关文章数据
		if ( $user_coin_id_list ) {
			// 币相关文章Redis
			$coinArticleRedis = new CoinArticleRedis();
			foreach ( $user_coin_id_list as $_coin_id ) {
				$user_coin_aw_list[$_coin_id] = $coinArticleRedis->GetCoinAllTypeArticleByCreateTime($_coin_id, $start, $end);
			}
		}

		// 置顶文章
		if ( $top_aw_id_list ) {
			// 文章列表Label 配置
			$label_cfg = PublishArticleModel::ArticleLabelConfig['top'];
			foreach ( $top_aw_id_list as $aw_id ) {
				if ( isset($top_aw_list[$aw_id]) ) {
					continue;
				}
				$unique_key = CommonFun::create_unique_id();
				list( $type, $id ) = explode("_", $aw_id);
				$top_aw_list[$aw_id] = array(
						"unique_key" => $unique_key,
						"type" => $type,
						"id" => $id,
						"coin_id" => 0,
						"label_name" => $label_cfg['label_name'],
						"label_icon" => $label_cfg['label_icon'],
						"user_coin_id_list" => [],
				);
			}
		}

		// 用户币种文章
		if ( $user_coin_id_list ) {
			$while_user = true;
			$while_user_asset_coin_id_list = $user_coin_id_list;
			do {
				if ( !$while_user_asset_coin_id_list ) {
					$while_user = false;
					break;
				}
				foreach ( $while_user_asset_coin_id_list as $wuac_key => $user_asset_coin_id ) {
					// 是否存在币种文章
					if ( !isset( $user_coin_aw_list[$user_asset_coin_id] ) ) {
						unset($while_user_asset_coin_id_list[$wuac_key]);
						continue;
					}
					if ( !$user_coin_aw_list[$user_asset_coin_id] ) {
						unset($while_user_asset_coin_id_list[$wuac_key]);
						continue;
					}

					$aw_id = array_shift($user_coin_aw_list[$user_asset_coin_id]);

					if ( !isset( $asset_aw_list[$aw_id] ) && !isset( $asset_aw_unessence_list[$aw_id] ) ) {
						// 文章列表Label 配置
// 						if (in_array($user_asset_coin_id, $user_asset_coin_id_list)) {
// 							$label_cfg_key = 'recommend';
// 						}else {
// 							$label_cfg_key = 'trend';
// 						}
// 						$label_cfg = PublishArticleModel::ArticleLabelConfig[$label_cfg_key];

						$unique_key = CommonFun::create_unique_id();
						list( $type, $id ) = explode("_", $aw_id);

						$allow = true;

						if ( $type == RecommendArticleTopModel::TypeArticle ) {
							if ( in_array($id, $userReadArticleList) ) {
								$allow = false;
							}
						}elseif ( $type == RecommendArticleTopModel::TypeWeiBo ) {
							if ( in_array($id, $userReadWeiboList) ) {
								$allow = false;
							}
						}
						if ( $allow ) {
							$_item = array(
									"unique_key" => $unique_key,
									"type" => $type,
									"id" => $id,
									"coin_id" => $user_asset_coin_id,
									"label_name" => '',
									"label_icon" => '',
									"user_coin_id_list" => $user_coin_id_list,
							);
							//  判断是否在加精区
							if (in_array($aw_id, $essence_aw_id_list)) {
								$label_cfg = PublishArticleModel::ArticleLabelConfig['essence'];
								//$_item['label_name'] = $label_cfg['label_name'];
								//$_item['label_icon'] = $label_cfg['label_icon'];
								$asset_aw_list[$aw_id] = $_item;
							}else {
								$asset_aw_unessence_list[$aw_id] = $_item;
								if ( $type == RecommendArticleTopModel::TypeArticle ) {
									$all_article_id_list[] = $id;
								}elseif ( $type == RecommendArticleTopModel::TypeWeiBo ) {
									$all_weibo_id_list[] = $id;
								}
							}
						}
					}

					if ( !$user_coin_aw_list[$user_asset_coin_id] ) {
						unset($while_user_asset_coin_id_list[$wuac_key]);
						break;
					}
				}
			}while ( $while_user );
		}

		// 获取热门文章 热门内容区：其他所有文章放在“热门内容区”，加精的排在前面（按时间，有效时间24h），非加精的排在后面（按时间）
		$hot_aw_id_list = [];
		$hot_essence_aw_id_list = [];
		$hot_article_map = array(
				'is_published' => 1,
				'status' => 1,
				'deleted' => -1,
				'is_feed' => 1,
				'coin_list' => array("eq", "[]"),
				'create_time' => array('between', array( $startDate, $endDate )),
				'is_tmp' => -1,
		);
		if ( $userReadArticleList ) {
			$hot_article_map['id'] = array( 'not in', $userReadArticleList );
		}
		$hot_article_id_list = $modelArticle->search_id_list_no_page( $hot_article_map, 'id', false, 'create_time' );
		foreach ( $hot_article_id_list as $haid ) {
			$aw = RecommendArticleTopModel::TypeArticle."_".$haid;
			if (  !in_array($aw, $essence_aw_id_list ) && !in_array($aw, $top_aw_id_list) ) {
				$hot_aw_id_list[] = $aw;
				$all_article_id_list[] = $haid;
			}elseif ( in_array($aw, $essence_aw_id_list) ) {
				$hot_essence_aw_id_list[] = $aw;
			}
		}
		$hot_weibo_map = array(
				'status' => 3, // 已通过
				'publish_status' => 2, //已发布
				'coin_list' => array("eq", "[]"),
				'create_time' => array('between', array( $startDate, $endDate )),
		);
		if ( $userReadWeiboList ) {
			$hot_weibo_map['id'] = array( 'not in', $userReadWeiboList );
		}
		$hot_weibo_id_list = $modelWeibo->search_id_list_no_page( $hot_weibo_map, 'id', false, 'create_time' );
		foreach ( $hot_weibo_id_list as $hwid ) {
			$aw = RecommendArticleTopModel::TypeWeiBo."_".$hwid;
			if (  !in_array($aw, $essence_aw_id_list ) && !in_array($aw, $top_aw_id_list) ) {
				$hot_aw_id_list[] = $aw;
				$all_weibo_id_list[] = $hwid;
			}elseif ( in_array($aw, $essence_aw_id_list) ) {
				$hot_essence_aw_id_list[] = $aw;
			}
		}
		// 热门加精文章
		if ( $hot_essence_aw_id_list ) {
			// 文章列表Label 配置
			$label_cfg = PublishArticleModel::ArticleLabelConfig['essence'];
			foreach ( $hot_essence_aw_id_list as $aw_id ) {
				if ( isset($hot_essence_aw_list[$aw_id]) ) {
					continue;
				}
				$unique_key = CommonFun::create_unique_id();
				list( $type, $id ) = explode("_", $aw_id);
				$hot_essence_aw_list[$aw_id] = array(
						"unique_key" => $unique_key,
						"type" => $type,
						"id" => $id,
						"coin_id" => 0,
						//"label_name" => $label_cfg['label_name'],
						//"label_icon" => $label_cfg['label_icon'],
						"label_name" => '',
						"label_icon" => '',
						"user_coin_id_list" => [],
				);
			}
		}

		// 热门非加精文章
		if ( $hot_aw_id_list ) {
			// 文章列表Label 配置
			$label_cfg = PublishArticleModel::ArticleLabelConfig['hot'];
			foreach ( $hot_aw_id_list as $aw_id ) {
				if ( isset($hot_aw_list[$aw_id]) ) {
					continue;
				}
				$unique_key = CommonFun::create_unique_id();
				list( $type, $id ) = explode("_", $aw_id);
				$hot_aw_list[$aw_id] = array(
						"unique_key" => $unique_key,
						"type" => $type,
						"id" => $id,
						"coin_id" => 0,
						"label_name" => '',
						"label_icon" => '',
						"user_coin_id_list" => [],
				);
			}
		}

		$hash_list = [];
		// 取数置顶数据
		if ( $top_aw_list ) {
			foreach ( $top_aw_list as $k=>$v ) {
				$hash_list[$k] = CommonFun::json_encode2($v);
			}
		}

		// 加精关注币种
		if ( $asset_aw_list ) {
			foreach ( $asset_aw_list as $k=>$v ) {
				if ( isset( $hash_list[$k] ) ) {
					continue;
				}
				$hash_list[$k] = CommonFun::json_encode2($v);
			}
		}

		// 热门加精文章
		if ( $hot_essence_aw_list ) {
			foreach ( $hot_essence_aw_list as $k=>$v ) {
				if ( isset( $hash_list[$k] ) ) {
					continue;
				}
				$hash_list[$k] = CommonFun::json_encode2($v);
			}
		}

		// 处理非加精所有文章
		$all_article_id_create_time = [];
		if ( $all_article_id_list ) {
			$where = array(
					'id' => array('in', $all_article_id_list),
					'is_feed' => 1 //排除不显示Feed流
			);
			$all_article_id_create_time = $modelArticle->field('id,create_time')->where($where)->getField('id,create_time');
		}
		$all_weibo_id_create_time = [];
		if ( $all_weibo_id_list ) {
			$where = array(
					'id' => array('in', $all_weibo_id_list),
			);
			$all_weibo_id_create_time = $modelWeibo->field('id,create_time')->where($where)->getField('id,create_time');
		}

		// 非加精文章
		$no_essence_aw_list = array_merge($hot_aw_list, $asset_aw_unessence_list);
		if ( $no_essence_aw_list ) {
			foreach ( $no_essence_aw_list as $nea_key => &$nev ) {
				$id = $nev['id'];
				$type = $nev['type'];
				if ( $type == RecommendArticleTopModel::TypeArticle ) {
					if ( !isset( $all_article_id_create_time[$id] ) ) {
						unset( $no_essence_aw_list[$nea_key] );
						continue;
					}
					$nev['create_time'] = strtotime($all_article_id_create_time[$id]);
				}elseif ( $type == RecommendArticleTopModel::TypeWeiBo ) {
					$nev['create_time'] = strtotime($all_weibo_id_create_time[$id]);
				}
			}
			CommonFun::array_sort($no_essence_aw_list, 'create_time', 'DESC', true, SORT_NATURAL);
			foreach ( $no_essence_aw_list as $k=>$v ) {
				if ( isset( $hash_list[$k] ) ) {
					continue;
				}
				$hash_list[$k] = CommonFun::json_encode2($v);
			}
		}
		//var_export($hash_list);

		// 文章Hash数据
		$key = "UserArticleHashTable:{$user_id}:{$client_sn}";
		$this->SetRedisKey($key);
		if ( $this->ExistsKey() ) {
			$this->Delete();
		}
		if ($hash_list) {
			$this->ListMutiRpush($hash_list);
			return true;
		}else {
			// 没有文章Hash数据重建数据
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeWeiBo);
			return false;
		}
	}

	/**
	 * 获取用户文章的列表
	 * @param int $user_id 用户编号
	 * @param int $client_sn 用户客户端唯一编号
	 * @param number $start 开始位置
	 * @param number $end 结束位置 -1为全部
	 */
	public function GetUserArticleList ( $user_id, $client_sn, $start=0, $end=-1 ) {
		$key = "UserArticleHashTable:{$user_id}:{$client_sn}";
		$this->SetRedisKey($key);
		$res = $this->ListlRange($start, $end);
		if ( $res ) {
			foreach ( $res as &$v ) {
				$v = json_decode($v, true);
			}
		}
		return $res;
	}

	/**
	 * 计算用户文章的列表数量
	 * @param int $user_id 用户编号
	 * @param int $client_sn 用户客户端唯一编号
	 * @return int
	 */
	public function CountUserArticleSize(  $user_id, $client_sn ) {
		$key = "UserArticleHashTable:{$user_id}:{$client_sn}";
		$this->SetRedisKey($key);
		$count = $this->ListSize();
		return  $count?:0;
	}




	//*********************************************************Feed流V3 Start*********************************************************************//

	/**
	 * 设置用户已曝光文章集合
	 * @param int $user_id 用户编号
	 * @param int|array $article_set 文章编号|编号列表
	 * @param string 	$type	文章类型
	 */
	public function setUserExposedArticleList( $user_id, $article_set, $type ) {
		$key = "UserExposedArticleList:{$user_id}:{$type}";
		$this->SetRedisKey($key);
		if ( is_array($article_set) ) {
			$res = $this->SetMutiSadd($article_set);
		}else {
			$res = $this->SetSadd($article_set);
		}
		return $res;
	}

	/**
	 * 获取用户已曝光文章集合
	 * @param int 		$user_id 	用户编号
	 * @param string 	$type		文章类型
	 * @return array
	 */
	public function getUserExposedArticleList ( $user_id, $type ) {
		$key = "UserExposedArticleList:{$user_id}:{$type}";
		$this->SetRedisKey($key);
		$res = $this->SetGetAll();
		return $res;
	}

	/**
	 * 删除用户已曝光文章集合
	 * @param int 		$user_id 	用户编号
	 * @param string 	$type		文章类型
	 */
	public function clearUserExposedArticleList (  $user_id, $type ) {
		$key = "UserExposedArticleList:{$user_id}:{$type}";
		$this->SetRedisKey($key);
		if ( $this->ExistsKey() ) {
			$this->Delete();
		}
	}

	/**
	 * 设置用户已曝光币种文章集合
	 * @param int $user_id 用户编号
	 * @param int $coin_id 币种编号
	 * @param int|array $article_set 文章编号|编号列表
	 * @param string $type 文章类型
	 */
	public function setUserExposedCoinArticleList ( $user_id, $coin_id, $article_set, $type ) {
		$key = "UserExposedCoinArticleList:{$user_id}:{$coin_id}:{$type}";
		$this->SetRedisKey($key);
		if ( is_array($article_set) ) {
			$res = $this->SetMutiSadd($article_set);
		}else {
			$res = $this->SetSadd($article_set);
		}
		return $res;
	}

	/**
	 * 获取用户已曝光币种文章集合
	 * @param int $user_id 用户编号
	 * @param int $coin_id 币种编号
	 * @param string $type 文章类型
	 * @return array
	 */
	public function getUserExposedCoinArticleList ( $user_id, $coin_id, $type) {
		$key = "UserExposedCoinArticleList:{$user_id}:{$coin_id}:{$type}";
		$this->SetRedisKey($key);
		$res = $this->SetGetAll();
		return $res;
	}

	/**
	 * 删除用户已曝光币种文章集合
	 * @param int 		$user_id 	用户编号
	 * @param int 		$coin_id 	币种编号
	 * @param string 	$type		文章类型
	 */
	public function clearUserExposedCoinArticleList ( $user_id, $coin_id, $type ) {
		$key = "UserExposedCoinArticleList:{$user_id}:{$coin_id}:{$type}";
		$this->SetRedisKey($key);
		if ( $this->ExistsKey() ) {
			$this->Delete();
		}
	}

	//*********************************************************Feed流V4 demo Start*********************************************************************//
	
	/**
	 * 获取按照CTR排序的文章列表
	 * @param bool $filter_read 是否过滤已读文章
	 * @param string $is_assoc  true返回一个关联数组 带分数
	 * @return array
	 */
	public function get_ctr_id_list( $filter_read=TRUE, $is_assoc=false ) {
		$ArticleCTRRedis = ArticleCTRRedis::getInstance();
		$res = $ArticleCTRRedis->SortSetZRevRange(0,-1, $is_assoc);
		if ( $filter_read && $this->userReadArticleList ) {
			foreach ( $res as $k=>$v ) {
				if ( in_array($v, $this->userReadArticleList) ) {
					unset($res[$k]);
				}
			}
		}
		return $res?:[];
	}
	
	/**
	 * 获取置顶文章的ID列表
	 * @param bool $filter_read 是否过滤已读文章
	 * @return array
	 */
	public function get_top_id_list( $limit=3, $filter_read=TRUE ) {
		$modelTop = RecommendArticleTopModel::getInstance();
		$time = time();
		$map = array(
				'start' => array('elt', $time),
				'end'	=> array('egt', $time),
				'type' => RecommendArticleTopModel::TypeArticle,
		);
		if ( $filter_read && $this->userReadArticleList ) {
			$map['article_id'] = array('not in', $this->userReadArticleList );
		}
		if ( $limit ) {
			$top_id_list = $modelTop->search_id_list($map, $page = 1, true, 'sort', $limit, 'article_id');
		}else {
			$top_id_list  = $modelTop->search_id_list_no_page($map, 'article_id', true, 'sort');
		}
		
		return $top_id_list;
	}
	
	/**
	 * 获取加精文章
	 * @param bool 		$filter_read	是否过滤已读文章
	 * @param number 	$offsetSecond 	时间范围(多长时间之前的数据), 单位秒, 默认 48小时, 0表示全部数据
	 * @return array
	 */
	public function get_essence_id_list(  $offsetSecond = 86400*2, $filter_read=TRUE ) {
		$modelEssence = PublishArticleEssenceModel::getInstance();
		$map = [];
		if ( $offsetSecond ) {
			$endDate = date("Y-m-d H:i:s");
			$startDate = date("Y-m-d H:i:s", time()-$offsetSecond);
			$map['create_time'] =  array('between', array( $startDate, $endDate ));
		}
		if ( $filter_read && $this->userReadArticleList ) {
			$map['id'] = array('not in', $this->userReadArticleList );
		}
		
		$essence_id_list = $modelEssence->search_id_list_no_page( $map, 'id', false, 'create_time' );
		
		return $essence_id_list;
	}
	
	/**
	 * 获取用户相关币种
	 * @param int $user_id 用户编号
	 * @return array
	 */
	public function get_user_ref_coin_id_list( $user_id ) {
		if ( !$user_id ) {
			return [];
		}
		// 用户持仓币列表
		$user_asset_coin_id_list = [];
		// 用户币id列表
		$user_coin_id_list = [];
		// 交易对模型
		$modePair = PairRuntimeModel::getInstance();
		// 用户交易对模型
		$modelUserPair  = UserPairModel::getInstance();
		// Python接口模型
		$servicePython = PythonApiService::getInstance();
		// 用户持仓币列表
		$user_asset_coin_id_list = $servicePython->getUserAssetCoinID($user_id);
		// 用户关注币列表
		$user_pair_coin_id_list = array();
		$user_pair_id_list = $modelUserPair->get_user_pair_id_list($user_id);
		if ( $user_pair_id_list ) {
			$modePair = PairRuntimeModel::getInstance();
			$pair_data = $modePair->muti_get_data_by_id_list($user_pair_id_list);
			$user_pair_coin_id_list = array_column($pair_data, "base");
		}
		
		// 用户所有币种编号列表
		$user_coin_id_list = array_unique(array_merge($user_asset_coin_id_list, $user_pair_coin_id_list));
		
		return $user_coin_id_list;
	}
	
	/**
	 * 获取用户币种aw列表
	 * @param array $user_coin_id_list 用户相关币种
	 * @param int $startTimestamp
	 * @param int $endTimestamp
	 * @param number $start
	 * @param number $end
	 * @param bool $is_assoc
	 * @return array
	 */
	public function get_user_coin_aw_list( array $user_coin_id_list, $startTimestamp, $endTimestamp,  $start=0, $end=-1,  $filter_read=TRUE, $is_assoc=false ) {
		if ( !$user_coin_id_list ) {
			return[];
		}
		// 用户币种aw列表
		$user_coin_aw_list = [];
		// 币相关文章Redis
		$coinArticleRedis = new CoinArticleRedis();
		foreach ( $user_coin_id_list as $_coin_id ) {
			
			$_list =  $coinArticleRedis->GetCoinAllTypeArticleByCreateTime($_coin_id, $startTimestamp, $endTimestamp, $start, $end, $is_assoc);
			if ( $_list && $filter_read && $this->userReadArticleList ) {
				foreach ( $_list as $_ak=>$_aw ) {
					list( $_type, $_article_id ) = explode("_", $_aw);
					if ( in_array($_article_id, $this->userReadArticleList) ) {
						unset($_list[$_ak]);
					}
				}
				
			}
			$user_coin_aw_list[$_coin_id] = $_list?:[];
		}
		
		return $user_coin_aw_list;
	}
	
	/**
	 * 构建Feed流V2
	 * @param int 		$user_id	用户编号
	 * @param string 	$client_sn	用户客户端唯一编号
	 * @param array 	$special_user_coin_id_list	指定用户持币列表 (主要用于非登录持币推荐)
	 */
	public function CreateUserArticleListV2( $user_id, $client_sn, array $special_user_coin_id_list=array() ) {
		// 用户币种模型
		$modelUserCoin = UserCoinModel::getInstance();
		$modelUserPair  = UserPairModel::getInstance();
		// 文章模型
		$modelArticle = PublishArticleModel::getInstance();
		$modelWeibo = PublishWeiboModel::getInstance();
		$modelTop = RecommendArticleTopModel::getInstance();
		$modelEssence = RecommendArticleEssenceModel::getInstance();
		// Python接口模型
		$servicePython = PythonApiService::getInstance();
		// 用户已读文章数据
		$user_mark = $user_id.":".$client_sn;
		$this->userReadArticleList = $this->getUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);
		$this->userReadWeiboList = $this->getUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeWeiBo);
		// 本次流程已存在文章编号
		$exist_article_id= [];
		// 取出24小时的文章
		$todayStartEndTime = CommonFun::getTodayStartEndTime();
		$end = $todayStartEndTime['end'];
		$endDate = date( "Y-m-d H:i:s", $end );
		$start =  $todayStartEndTime['start']-(86400*10);
		$startDate =  date( "Y-m-d H:i:s", $start );
		$noCoinStartDate = date( "Y-m-d H:i:s", $todayStartEndTime['start']-(86400*10) );
		
		// 获取置顶文章
		$top_id_list = $this->get_top_id_list(0);
		// 获取加精文章->按照create_time的降序排序
		$essence_id_list = $this->get_essence_id_list(86400*100);
		// 加精文章按照ctr排序和create_time排序
		$ess_ctr_sort_list = [];
		
		// CTR文章
		$ctr_id_list = $this->get_ctr_id_list();
		if ( $ctr_id_list ) {
			$flip_ctr =  array_flip($ctr_id_list);
			$max_ctr_sort = max($flip_ctr)+1;
			foreach ( $essence_id_list as $_ek => $_eid ) {
				if ( isset( $flip_ctr[$_eid] ) ) {
					$_k = $flip_ctr[$_eid];
				}else {
					$_k = $max_ctr_sort+$_ek;
				}
				$ess_ctr_sort_list[$_k] = $_eid;
			}
			ksort($ess_ctr_sort_list);
		}else {
			$ess_ctr_sort_list = $essence_id_list;
		}
		
		// 顶部热门文章
		$htop_id_list = array_slice(array_unique(array_merge($top_id_list, $ess_ctr_sort_list)), 0 , 3);
		$top_aw_list = [];
		if ( $htop_id_list ) {
			// 文章列表Label 配置
			$label_cfg = PublishArticleModel::ArticleLabelConfig['top'];
			foreach ( $htop_id_list as $article_id ) {
				$aw_id =  RecommendArticleTopModel::TypeArticle."_".$article_id;
				if ( isset($top_aw_list[$aw_id]) ) {
					continue;
				}
				if ( in_array($article_id, $exist_article_id) ) {
					continue;
				}
				$exist_article_id[] = $article_id;
				$top_aw_list[$aw_id] = array(
						"unique_key" => CommonFun::create_unique_id(),
						"type" => RecommendArticleTopModel::TypeArticle,
						"id" => $article_id,
						"coin_id" => 0,
						"label_name" => $label_cfg['label_name'],
						"label_icon" => $label_cfg['label_icon'],
						"user_coin_id_list" => [],
				);
			}
		}
		
		// 用户币id列表
		$user_coin_id_list = $special_user_coin_id_list ?:$this->get_user_ref_coin_id_list($user_id);
		
		// 用户币种aw列表
		$user_coin_aw_list = $user_coin_id_list ? $this->get_user_coin_aw_list($user_coin_id_list, $start, $end) : [];
		
		// 币种文章
		$user_aw_list = [];
		if ( $user_coin_aw_list ) {
			foreach ( $user_coin_aw_list as $_cid=>$_aw_list ) {
				$_article_id_list = [];
				foreach ( $_aw_list as $_aw_id ) {
					list( $_type, $_article_id ) = explode("_", $_aw_id);
					if ( $_type == RecommendArticleTopModel::TypeWeiBo ) {
						continue;
					}
					if ( in_array($_article_id, $exist_article_id) ) {
						continue;
					}
					$exist_article_id[] = $_article_id;
					if ( isset( $top_aw_list[ RecommendArticleTopModel::TypeArticle."_".$_article_id ] ) ) {
						continue;
					}
					$_article_id_list[] = $_article_id;
				}
				if ( $_article_id_list ) {
					// 判断文章是否是加精文章
					$is_essence_list = $modelEssence->isEssence($_article_id_list, RecommendArticleEssenceModel::TypeArticle);
					// 加精文章靠前
					arsort($is_essence_list);
					$_article_id_list = array_keys(array_slice($is_essence_list, 0, 3, true));
					array_map(function( $_vid ) use(&$user_aw_list, $_cid, $user_coin_id_list){
						$_aw_k = RecommendArticleTopModel::TypeArticle."_".$_vid;
						$_item = array(
								"unique_key" => CommonFun::create_unique_id(),
								"type" => RecommendArticleTopModel::TypeArticle,
								"id" => $_vid,
								"coin_id" => $_cid,
								"label_name" => '',
								"label_icon" => '',
								"user_coin_id_list" => $user_coin_id_list,
						);
						$user_aw_list[$_aw_k] = $_item;
					}, $_article_id_list);
				}
			}
		}
		
		// 最终返回列表
		$hash_list = [];
		
		if ( $top_aw_list ) {
			foreach ( $top_aw_list as $k=>$v ) {
				//$hash_list[$k] = CommonFun::json_encode2($v);
				$hash_list[$k] = $v;
			}
		}
		
		if ( $user_aw_list ) {
			foreach ( $user_aw_list as $k=>$v ) {
				//$hash_list[$k] = CommonFun::json_encode2($v);
				$hash_list[$k] = $v;
			}
		}
		
		if ( $hash_list ) {
			return array_values($hash_list);
		}else {
			// 没有文章Hash数据重建数据
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeWeiBo);
			return false;
		}
	}


	/**
	 * 获取按照CTR排序的文章列表
	 */
	public function getArticleCtrList($order='desc') {
		$ArticleCTRRedis = ArticleCTRRedis::getInstance();
		if ($order =='desc') {
			$res = $ArticleCTRRedis->SortSetZRevRange(0, -1, true);
		} elseif ($order == 'asc') {
			$res = $ArticleCTRRedis->SortSetZRange(0, -1, true);
		}
		
		return $res?:[];
	}
	
	//*********************************************************Feed流V5 Start*********************************************************************//
	
	/**
	 * 构建Feed流V3
	 * @param int 		$user_id	用户编号
	 * @param string 	$client_sn	用户客户端唯一编号
	 * @param array 	$special_user_coin_id_list	指定用户持币列表 (主要用于非登录持币推荐)
	 *
	 * FEED流文章时效性要求：无论币种非币种文章，后台打上时效性标签的只有1天；非时效未加优质库的3天有效，非时效加优质库（优质库）的永远有效
	 * 置顶区：手动置顶，每个方案都有，纯人工运营。正常不运营。
	 * 优质文章区（5篇。第一个位置给24h内加优质库的文章，按CTR决定出现顺序。第二个位置给24h内新加优质库没有CTR曝光。3-10位置给优质库，按CTR排序）
	 * 普通文章（3天内非币种和我关注的币种文章）按时间（PS：或CTR）倒序
	 */
	public function CreateUserArticleListV3( $user_id, $client_sn, array $special_user_coin_id_list=array() ) {
		// 用户币种模型
		$modelUserCoin = UserCoinModel::getInstance();
		$modelUserPair  = UserPairModel::getInstance();
		// 文章模型
		$modelArticle = PublishArticleModel::getInstance();
		$modelCoinArticle = PublishArticleCoinModel::getInstance();
		$modelWeibo = PublishWeiboModel::getInstance();
		$modelTop = RecommendArticleTopModel::getInstance();
		$modelEssence = RecommendArticleEssenceModel::getInstance();
		// Python接口模型
		$servicePython = PythonApiService::getInstance();
		// 用户已读文章数据
		$user_mark = $user_id.":".$client_sn;
		$this->userReadArticleList = $this->getUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);
		$this->userReadWeiboList = $this->getUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeWeiBo);
		// 本次流程已存在文章编号
		$exist_article_id = [];
		// 取出24小时的文章
		$todayStartEndTime = CommonFun::getTodayStartEndTime();
		$end = $todayStartEndTime['end'];
		$endDate = date( "Y-m-d H:i:s", $end );
		$start =  $todayStartEndTime['start']-(86400*10);
		$startDate =  date( "Y-m-d H:i:s", $start );
		$_startDate =  date( "Y-m-d H:i:s",  $todayStartEndTime['start']-86400 );
		$noCoinStartDate = date( "Y-m-d H:i:s", $todayStartEndTime['start']-(86400*3) );
		
		// 顶部文章aw列表
		$top_aw_list = [];
		
		// 置顶区 获取置顶文章 文章编号列表->未排除已读
		$top_id_list = $exist_article_id = $this->get_top_id_list(0, false);
		
		// CTR文章排序 文章编号->未排除已读
		$ctr_id_list = $this->get_ctr_id_list(false);
		$ctr_id_list = $ctr_id_list ? array_diff($ctr_id_list, $this->userReadArticleList) : $ctr_id_list;
		
		// 24h加精库文章->未排除已读
		$essence_id_list = $this->get_essence_id_list(86400, false);
		$essence_id_list = $essence_id_list? array_diff($essence_id_list, $this->userReadArticleList) : $essence_id_list;
		$ess_ctr_sort_list = [];
		// 所有加精库->未排除已读
		$all_essence_id_list = $this->get_essence_id_list(0, false);
		$all_essence_id_list = $all_essence_id_list? array_diff($all_essence_id_list, $this->userReadArticleList) : $all_essence_id_list;
		$all_ess_ctr_sort_list = [];
		if ( $ctr_id_list ) {
			$flip_ctr =  array_flip($ctr_id_list);
			$max_ctr_sort = max($flip_ctr)+1;
			foreach ( $essence_id_list as $_ek => $_eid ) {
				if ( isset( $flip_ctr[$_eid] ) ) {
					$_k = $flip_ctr[$_eid];
				}else {
					$_k = $max_ctr_sort+$_ek;
				}
				$ess_ctr_sort_list[$_k] = $_eid;
			}
			ksort($ess_ctr_sort_list);
			foreach ( $all_essence_id_list as $_ek => $_eid ) {
				if ( isset( $flip_ctr[$_eid] ) ) {
					$_k = $flip_ctr[$_eid];
				}else {
					$_k = $max_ctr_sort+$_ek;
				}
				$all_ess_ctr_sort_list[$_k] = $_eid;
			}
			ksort($all_ess_ctr_sort_list);
		}else {
			$ess_ctr_sort_list = $essence_id_list;
			$all_ess_ctr_sort_list = $all_essence_id_list;
		}
		
		// 第一个位置给24h内加优质库的文章，按CTR决定出现顺序
		// $intersect_ctr_ess = array_intersect($ctr_id_list, $essence_id_list); // 交集
		$intersect_ctr_ess = array_diff($ess_ctr_sort_list, $exist_article_id); // 差集排除
		if ( $intersect_ctr_ess ) {
			$firstPosiArticleID = array_shift($intersect_ctr_ess);
			$top_id_list[] = $exist_article_id[] = $firstPosiArticleID;
		}
		// 第二个位置给24h内新加优质库没有CTR曝光
		$diff_ess_ctr = array_diff($essence_id_list, $ctr_id_list, $exist_article_id);
		if ( $diff_ess_ctr ) {
			$secondPosiArticleID = array_shift($diff_ess_ctr);
			$top_id_list[] = $exist_article_id[] = $secondPosiArticleID;
		}
		// 3-20位置给全部优质库，按CTR排序, 无时间限制 (取24小时内的时效性文章, 和全部非时效性文章)
		$intersect_ctr_ess = array_diff($all_ess_ctr_sort_list, $exist_article_id);
		if ( $intersect_ctr_ess ) {
			$modelArticleEss = PublishArticleEssenceModel::getInstance();
			// 取24小时之外的时效性文章
			$out24h_is_time_map = [
					'id' => array('in', $intersect_ctr_ess),
					'is_time' => 1,
					'create_time' => array( 'lt',  $_startDate ),
			];
			$out24h_is_time_list = $modelArticleEss->search_id_list_no_page( $out24h_is_time_map );
			// 过滤掉24小时之外的时效性文章
			$intersect_ctr_ess = array_diff($intersect_ctr_ess, $out24h_is_time_list);
			if ( $intersect_ctr_ess ) {
				for ( $i=0;$i<=19;$i++ ) {
					$pArticleID = array_shift($intersect_ctr_ess);
					if ($pArticleID) {
						$top_id_list[] = $exist_article_id[] = $pArticleID;
					}
				}
			}
		}
		// 顶部热门文章构建
		$top_id_list = $top_id_list ? array_diff($top_id_list, $this->userReadArticleList) : $top_id_list;
		if ( $top_id_list ) {
			// 文章列表Label 配置
			$label_cfg = PublishArticleModel::ArticleLabelConfig['top'];
			foreach ( $top_id_list as $article_id ) {
				$aw_id =  RecommendArticleTopModel::TypeArticle."_".$article_id;
				if ( isset($top_aw_list[$aw_id]) ) {
					continue;
				}
				$exist_article_id[] = $article_id;
				$top_aw_list[$aw_id] = array(
						"unique_key" => CommonFun::create_unique_id(),
						"type" => RecommendArticleTopModel::TypeArticle,
						"id" => $article_id,
						"coin_id" => 0,
						"label_name" => '',
						"label_icon" => '',
						"user_coin_id_list" => [],
				);
			}
		}
		
		// 币种文章aw列表
		$coin_article_aw_list = [];
		// 用户相关币种id列表
		$user_coin_id_list = $special_user_coin_id_list ?:$this->get_user_ref_coin_id_list($user_id);
		// 获取币种文章
		if ( $user_coin_id_list ) {
			// 币种文章Redis
			$coinArticleRedis = new CoinArticleRedis();
			// 获取非时效性文章
			$no_is_time_article_list = $coinArticleRedis->getDateCoinArticleList($user_coin_id_list, 1, -1, $startDate, 11);
			// 获取时效性文章
			$is_time_article_list = $coinArticleRedis->getDateCoinArticleList($user_coin_id_list, 1, 1, $_startDate, 2);
			// 合并数据, 获取币种文章
			$coin_article_list = [];
			foreach ( $user_coin_id_list as $userCoin ) {
				$a_1 = [];
				if ( isset( $no_is_time_article_list[$userCoin] ) && $no_is_time_article_list[$userCoin] ) {
					$a_1 = $no_is_time_article_list[$userCoin];
				}
				$a_2 = [];
				if ( isset( $is_time_article_list[$userCoin] ) &&  $is_time_article_list[$userCoin] ) {
					$a_2 = $is_time_article_list[$userCoin];
				}
				
				$coin_article_list[$userCoin] = array_unique( array_merge($a_1, $a_2));
			}
			// 排除文章
			$note_in_article = [];
			if ( $exist_article_id ) {
				$note_in_article = array_merge($note_in_article, $exist_article_id);
			}
			if ( $this->userReadArticleList ) {
				$note_in_article = array_merge($note_in_article, $this->userReadArticleList);
			}
			if ( $all_essence_id_list ) {
				$note_in_article = array_merge($note_in_article, $all_essence_id_list);
			}
			// 币种文章整理
			if ( $coin_article_list ) {
				foreach ( $coin_article_list as $_cid=>$_art_list ) {
					if ( !$_art_list ) {
						continue;
					}
					$exist_article_id = array_merge($exist_article_id, $_art_list);
					
					// 遍历文章编号列表
					foreach ( $_art_list as $_art_id ) {
						if ( in_array($_art_id, $note_in_article) ) {
							continue;
						}
						$_aw_k = RecommendArticleTopModel::TypeArticle."_".$_art_id;
						if ( isset( $coin_article_aw_list[$_aw_k] ) ) {
							continue;
						}
						$_item = array(
								"unique_key" => CommonFun::create_unique_id(),
								"type" => RecommendArticleTopModel::TypeArticle,
								"id" => $_art_id,
								"coin_id" => $_cid,
								"label_name" => '',
								"label_icon" => '',
								"user_coin_id_list" => $user_coin_id_list,
						);
						$coin_article_aw_list[$_aw_k] = $_item;
					}
				}
			}
		}
		
		// 非币种文章
		$no_coin_article_aw_list = [];
		$note_in_article = [];
		$no_coin_map = array(
				'is_published' => 1,
				'status' => 1,
				'deleted' => -1,
				'is_feed' => 1,
				'coin_list' => array("eq", "[]"),
				'create_time' => array('between', array( $startDate, $endDate )),
				'is_tmp' => -1,
				'article_type' => array('neq', PublishArticleModel::CMS_TYPE_DATADAILY),
				// 三天非时效性 和 1天时效性
				"_string" => " ( `is_time`=-1 AND `create_time` between '{$startDate}' AND '{$endDate}' ) OR ( `is_time`=1 AND `create_time` between '{$_startDate}' AND '{$endDate}' )  ",
		);
		if ( $exist_article_id ) {
			$note_in_article = array_merge($note_in_article, $exist_article_id);
		}
		if ( $this->userReadArticleList ) {
			$note_in_article = array_merge($note_in_article, $this->userReadArticleList);
		}
		if ( $all_essence_id_list ) {
			$note_in_article = array_merge($note_in_article, $all_essence_id_list);
		}
		if ( $note_in_article ) {
			$no_coin_map['id'] = array('not in', $note_in_article);
		}
		$no_coin_article_list = $modelArticle->search_id_list_no_page($no_coin_map);
		if ( $no_coin_article_list ) {
			$exist_article_id = array_merge($exist_article_id, $no_coin_article_list);
			foreach ( $no_coin_article_list as $_art_id ) {
				$_aw_k = RecommendArticleTopModel::TypeArticle."_".$_art_id;
				if ( isset( $coin_article_aw_list[$_aw_k] ) || isset($no_coin_article_aw_list[$_aw_k]) ) {
					continue;
				}
				$_item = array(
						"unique_key" => CommonFun::create_unique_id(),
						"type" => RecommendArticleTopModel::TypeArticle,
						"id" => $_art_id,
						"coin_id" => 0,
						"label_name" => '',
						"label_icon" => '',
						"user_coin_id_list" => [],
				);
				$no_coin_article_aw_list[$_aw_k] = $_item;
			}
		}
		
		// 总结数据
		$normal_aw_list = array_merge($coin_article_aw_list, $no_coin_article_aw_list);
		if ( $normal_aw_list ) {
			CommonFun::array_sort($normal_aw_list, "id");
		}
		
		// 最终返回列表
		$hash_list = [];
		
		if ( $top_aw_list ) {
			foreach ( $top_aw_list as $k=>$v ) {
				$hash_list[] = CommonFun::json_encode2($v);
			}
		}
		
		if ( $normal_aw_list ) {
			foreach ( $normal_aw_list as $k=>$v ) {
				$hash_list[] = CommonFun::json_encode2($v);
			}
		}
		
		// 文章Hash数据
		$key = "UserArticleHashTable:{$user_id}:{$client_sn}";
		$this->SetRedisKey($key);
		if ( $this->ExistsKey() ) {
			$this->Delete();
		}
		if ($hash_list) {
			$this->ListMutiRpush($hash_list);
			return true;
		}else {
			// 没有文章Hash数据重建数据
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeWeiBo);
			return false;
		}
		
	}
	
	//*********************************************************Feed流V6 Start*********************************************************************//
	
	/**
	 * 构建Feed流V4
	 * @param int 		$user_id	用户编号
	 * @param string 	$client_sn	用户客户端唯一编号
	 * @param array 	$special_user_coin_id_list	指定用户持币列表 (主要用于非登录持币推荐)
	 *
	 * FEED流文章时效性要求：无论币种非币种文章，后台打上时效性标签的只有1天；非时效未加优质库的3天有效，非时效加优质库（优质库）的永远有效
	 * 置顶区：手动置顶，每个方案都有，纯人工运营。正常不运营。
	 * 优质文章区（5篇。第一个位置给24h内加优质库的文章，按CTR决定出现顺序。第二个位置给24h内新加优质库没有CTR曝光。3-10位置给优质库，按CTR排序）
	 * 普通文章（3天内非币种和我关注的币种文章）按CTR，CTR方案时，按照高于6%区域按倒序+没CTR区域按时间+低CTR区域倒序
	 */
	public function CreateUserArticleListV4( $user_id, $client_sn, array $special_user_coin_id_list=array() ) {
		// 用户币种模型
		$modelUserCoin = UserCoinModel::getInstance();
		$modelUserPair  = UserPairModel::getInstance();
		// 文章模型
		$modelArticle = PublishArticleModel::getInstance();
		$modelCoinArticle = PublishArticleCoinModel::getInstance();
		$modelWeibo = PublishWeiboModel::getInstance();
		$modelTop = RecommendArticleTopModel::getInstance();
		$modelEssence = RecommendArticleEssenceModel::getInstance();
		// Python接口模型
		$servicePython = PythonApiService::getInstance();
		// 用户已读文章数据
		$user_mark = $user_id.":".$client_sn;
		$this->userReadArticleList = $this->getUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);
		$this->userReadWeiboList = $this->getUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeWeiBo);
		// 本次流程已存在文章编号
		$exist_article_id= [];
		// 取出24小时的文章
		$todayStartEndTime = CommonFun::getTodayStartEndTime();
		$end = $todayStartEndTime['end'];
		$endDate = date( "Y-m-d H:i:s", $end );
		$start =  $todayStartEndTime['start']-(86400*10);
		$startDate =  date( "Y-m-d H:i:s", $start );
		$_startDate =  date( "Y-m-d H:i:s",  $todayStartEndTime['start']-86400 );
		$noCoinStartDate = date( "Y-m-d H:i:s", $todayStartEndTime['start']-(86400*3) );
		
		// 顶部文章aw列表
		$top_aw_list = [];
		
		// 置顶区 获取置顶文章 文章编号列表->以排除已读
		$top_id_list = $exist_article_id = $this->get_top_id_list(0);
		
		// CTR文章排序 文章编号->以排除已读
		$ctr_id_score_list = $this->get_ctr_id_list(true, true);
		$ctr_id_list = array_keys($ctr_id_score_list);
		
		//普通文章分区界限
		$area_limit_ctr = 600;
		
		// 24h加精库文章->以排除已读
		$essence_id_list = $this->get_essence_id_list(86400);
		$ess_ctr_sort_list = [];
		// 所有加精库
		$all_essence_id_list = $this->get_essence_id_list(0);
		$all_ess_ctr_sort_list = [];
		if ( $ctr_id_list ) {
			$flip_ctr =  array_flip($ctr_id_list);
			$max_ctr_sort = max($flip_ctr)+1;
			foreach ( $essence_id_list as $_ek => $_eid ) {
				if ( isset( $flip_ctr[$_eid] ) ) {
					$_k = $flip_ctr[$_eid];
				}else {
					$_k = $max_ctr_sort+$_ek;
				}
				$ess_ctr_sort_list[$_k] = $_eid;
			}
			ksort($ess_ctr_sort_list);
			foreach ( $all_essence_id_list as $_ek => $_eid ) {
				if ( isset( $flip_ctr[$_eid] ) ) {
					$_k = $flip_ctr[$_eid];
				}else {
					$_k = $max_ctr_sort+$_ek;
				}
				$all_ess_ctr_sort_list[$_k] = $_eid;
			}
			ksort($all_ess_ctr_sort_list);
		}else {
			$ess_ctr_sort_list = $essence_id_list;
			$all_ess_ctr_sort_list = $all_essence_id_list;
		}
		
		// 第一个位置给24h内加优质库的文章，按CTR决定出现顺序
		// $intersect_ctr_ess = array_intersect($ctr_id_list, $essence_id_list); // 交集
		$intersect_ctr_ess = array_diff($ess_ctr_sort_list, $exist_article_id); // 差集排除
		if ( $intersect_ctr_ess ) {
			$firstPosiArticleID = array_shift($intersect_ctr_ess);
			$top_id_list[] = $exist_article_id[] = $firstPosiArticleID;
		}
		// 第二个位置给24h内新加优质库没有CTR曝光
		$diff_ess_ctr = array_diff($essence_id_list, $ctr_id_list, $exist_article_id);
		if ( $diff_ess_ctr ) {
			$secondPosiArticleID = array_shift($diff_ess_ctr);
			$top_id_list[] = $exist_article_id[] = $secondPosiArticleID;
		}
		// 3-5位置给全部优质库，按CTR排序, 无时间限制 (取24小时内的时效性文章, 和全部非时效性文章)
		$intersect_ctr_ess = array_diff($all_ess_ctr_sort_list, $exist_article_id);
		if ( $intersect_ctr_ess ) {
			$modelArticleEss = PublishArticleEssenceModel::getInstance();
			// 取24小时之外的时效性文章
			$out24h_is_time_map = [
					'id' => array('in', $intersect_ctr_ess),
					'is_time' => 1,
					'create_time' => array( 'lt',  $_startDate ),
			];
			$out24h_is_time_list = $modelArticleEss->search_id_list_no_page( $out24h_is_time_map );
			// 过滤掉24小时之外的时效性文章
			$intersect_ctr_ess = array_diff($intersect_ctr_ess, $out24h_is_time_list);
			if ( $intersect_ctr_ess ) {
				for ( $i=0;$i<=19;$i++ ) {
					$pArticleID = array_shift($intersect_ctr_ess);
					if ($pArticleID) {
						$top_id_list[] = $exist_article_id[] = $pArticleID;
					}
				}
			}
		}
		// 顶部热门文章构建
		if ( $top_id_list ) {
			// 文章列表Label 配置
			$label_cfg = PublishArticleModel::ArticleLabelConfig['top'];
			foreach ( $top_id_list as $article_id ) {
				$aw_id =  RecommendArticleTopModel::TypeArticle."_".$article_id;
				if ( isset($top_aw_list[$aw_id]) ) {
					continue;
				}
				$exist_article_id[] = $article_id;
				$top_aw_list[$aw_id] = array(
						"unique_key" => CommonFun::create_unique_id(),
						"type" => RecommendArticleTopModel::TypeArticle,
						"id" => $article_id,
						"coin_id" => 0,
						"label_name" => '',
						"label_icon" => '',
						"user_coin_id_list" => [],
				);
			}
		}
		
		// 币种文章aw列表
		$coin_article_aw_list = [];
		// 用户相关币种id列表
		$user_coin_id_list = $special_user_coin_id_list ?:$this->get_user_ref_coin_id_list($user_id);
		// 获取币种文章
		if ( $user_coin_id_list ) {
			// 币种文章额外条件
			$coin_article_map = array(
					'create_time' => array('between', array($startDate, $endDate)),
			);
			$note_in_article = [];
			if ( $exist_article_id ) {
				$note_in_article = array_merge($note_in_article, $exist_article_id);
			}
			if ( $this->userReadArticleList ) {
				$note_in_article = array_merge($note_in_article, $this->userReadArticleList);
			}
			if ( $all_essence_id_list ) {
				$note_in_article = array_merge($note_in_article, $all_essence_id_list);
			}
			if ( $note_in_article ) {
				$coin_article_map['article_id'] = array('not in', $note_in_article);
			}
			// 获取币种文章
			$coin_article_list = $modelCoinArticle->get_coin_article_list($user_coin_id_list, $coin_article_map);
			if ( $coin_article_list ) {
				// 检查文章是否合法
				$check_map = array(
						'is_published' => 1,
						'status' => 1,
						'deleted' => -1,
						'is_feed' => 1,
						'is_tmp' => -1,
						'article_type' => array('neq', PublishArticleModel::CMS_TYPE_DATADAILY),
						// 三天非时效性 和 1天时效性
						"_string" => " ( `is_time`=-1 AND `create_time` between '{$startDate}' AND '{$endDate}' ) OR ( `is_time`=1 AND `create_time` between '{$_startDate}' AND '{$endDate}' )  ",
				);
				foreach ( $coin_article_list as $_cid=>$_art_list ) {
					if ( !$_art_list ) {
						continue;
					}
					$exist_article_id = array_merge($essence_id_list, $_art_list);
					$check_map['id'] = array('in', $_art_list);
					$_art_list = $modelArticle->search_id_list_no_page($check_map);
					if ( !$_art_list ) {
						continue;
					}
					// 遍历文章编号列表
					foreach ( $_art_list as $_art_id ) {
						$_aw_k = RecommendArticleTopModel::TypeArticle."_".$_art_id;
						if ( isset( $coin_article_aw_list[$_aw_k] ) ) {
							continue;
						}
						// 构建排序标识
						if ( isset( $ctr_id_score_list[$_art_id] ) ) {
							$score = $ctr_id_score_list[$_art_id];
							$mark = $score >=$area_limit_ctr ? $score+3 : $score+1 ;
						}else {
							$mark =  $area_limit_ctr+2;
						}
						$sort = $mark."_".$_art_id;
						$_item = array(
								"unique_key" => CommonFun::create_unique_id(),
								"type" => RecommendArticleTopModel::TypeArticle,
								"id" => $_art_id,
								"coin_id" => $_cid,
								"label_name" => '',
								"label_icon" => '',
								"sort" => $sort,
								"user_coin_id_list" => $user_coin_id_list,
						);
						$coin_article_aw_list[$_aw_k] = $_item;
					}
				}
			}
		}
		
		// 非币种文章
		$no_coin_article_aw_list = [];
		$note_in_article = [];
		$no_coin_map = array(
				'is_published' => 1,
				'status' => 1,
				'deleted' => -1,
				'is_feed' => 1,
				'coin_list' => array("eq", "[]"),
				'create_time' => array('between', array( $startDate, $endDate )),
				'is_tmp' => -1,
				'article_type' => array('neq', PublishArticleModel::CMS_TYPE_DATADAILY),
				// 三天非时效性 和 1天时效性
				"_string" => " ( `is_time`=-1 AND `create_time` between '{$startDate}' AND '{$endDate}' ) OR ( `is_time`=1 AND `create_time` between '{$_startDate}' AND '{$endDate}' )  ",
		);
		if ( $exist_article_id ) {
			$note_in_article = array_merge($note_in_article, $exist_article_id);
		}
		if ( $this->userReadArticleList ) {
			$note_in_article = array_merge($note_in_article, $this->userReadArticleList);
		}
		if ( $all_essence_id_list ) {
			$note_in_article = array_merge($note_in_article, $all_essence_id_list);
		}
		if ( $note_in_article ) {
			$no_coin_map['id'] = array('not in', $note_in_article);
		}
		$no_coin_article_list = $modelArticle->search_id_list_no_page($no_coin_map);
		if ( $no_coin_article_list ) {
			$exist_article_id = array_merge($exist_article_id, $no_coin_article_list);
			foreach ( $no_coin_article_list as $_art_id ) {
				$_aw_k = RecommendArticleTopModel::TypeArticle."_".$_art_id;
				if ( isset( $coin_article_aw_list[$_aw_k] ) || isset($no_coin_article_aw_list[$_aw_k]) ) {
					continue;
				}
				// 构建排序标识
				if ( isset( $ctr_id_score_list[$_art_id] ) ) {
					$score = $ctr_id_score_list[$_art_id];
					$mark = $score >=$area_limit_ctr ? $score+3 : $score+1 ;
				}else {
					$mark =  $area_limit_ctr+2;
				}
				$sort = $mark."_".$_art_id;
				$_item = array(
						"unique_key" => CommonFun::create_unique_id(),
						"type" => RecommendArticleTopModel::TypeArticle,
						"id" => $_art_id,
						"coin_id" => 0,
						"label_name" => '',
						"label_icon" => '',
						"sort" => $sort,
						"user_coin_id_list" => [],
				);
				$no_coin_article_aw_list[$_aw_k] = $_item;
			}
		}
		
		// 总结数据
		$normal_aw_list = array_merge($coin_article_aw_list, $no_coin_article_aw_list);
		if ( $normal_aw_list ) {
			CommonFun::array_sort($normal_aw_list, "sort");
		}
		
		// 最终返回列表
		$hash_list = [];
		
		if ( $top_aw_list ) {
			foreach ( $top_aw_list as $k=>$v ) {
				$hash_list[] = CommonFun::json_encode2($v);
			}
		}
		
		if ( $normal_aw_list ) {
			foreach ( $normal_aw_list as $k=>$v ) {
				$hash_list[] = CommonFun::json_encode2($v);
			}
		}
		
		// 文章Hash数据
		$key = "UserArticleHashTable:{$user_id}:{$client_sn}";
		$this->SetRedisKey($key);
		if ( $this->ExistsKey() ) {
			$this->Delete();
		}
		if ($hash_list) {
			$this->ListMutiRpush($hash_list);
			return true;
		}else {
			// 没有文章Hash数据重建数据
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeArticle);
			$this->clearUserExposedArticleList($user_mark, RecommendArticleTopModel::TypeWeiBo);
			return false;
		}
		
	}


















}